package com.meshnetics.gb.type;

/**
 * Abstract class for mandatory data type formatter. Each such formatter supports values only of this type.
 * Mandatory formatter performs serialization/deserialization to/from String. Supported formats:
 * <ul>
 * <li><code>FORMAT_SERIALIZED</code> - serialized to String form of value;
 * <li><code>FORMAT_SHORT_LOG</code> - compact string format for usages in logs (basicly name of type and very short
 * but readable representation of value expected);
 * <li><code>FORMAT_MAX_LOG</code> - maximum info on value is expected in this format (for example class names of
 * type and value, values of inner fields of value object or just <code>FORMAT_SHORT_LOG</code>
 * string representation of value).
 * </ul>
 *
 * @author AKovylin
 */
public abstract class MandatoryFormatter implements Formatter {
    /**
     * Serialization to String.
     */
    public static final int FORMAT_SERIALIZED = -1;
    /**
     * Compact info for logging.
     */
    public static final int FORMAT_SHORT_LOG = -2;
    /**
     * Maximum possible info for logging.
     */
    public static final int FORMAT_MAX_LOG = -3;

    protected final DataType dataType;

    /**
     * Creates mandatory formatter for specified data type, no other data types are supported.
     *
     * @param dataType data type
     */
    protected MandatoryFormatter(DataType dataType) {
        if (dataType == null) {
            throw new NullPointerException("dataType is null");
        }
        this.dataType = dataType;
    }

    /**
     * Returns predefined name as
     * <code>"Mandatory formatter for " + valueType.getName() + " (" + valueType.getClass().getName() + ')'</code>
     *
     * @return {@inheritDoc}
     */
    public String getFormatterName() {
        return "Mandatory formatter for " + dataType.getName() + " (" + dataType.getClass().getName() + ')';
    }

    /**
     * Returns <code>true</code> only for three formats specified in this formatter.
     *
     * @param format {@inheritDoc}
     * @return {@inheritDoc}
     */
    public boolean isFormatSupported(int format) {
        return format == FORMAT_SERIALIZED || format == FORMAT_SHORT_LOG || format == FORMAT_MAX_LOG;
    }

    /**
     * Returns <code>true</code> only for valueType of same class and equal
     * to data type that have been passed in constructor.
     * No other data types supported.
     *
     * @param valueType {@inheritDoc}
     * @return {@inheritDoc}
     */
    public boolean isTypeSupported(DataType valueType) {
        return valueType != null &&
                this.dataType.getClass() == valueType.getClass() && this.dataType.equals(valueType);
    }

    /**
     * {@inheritDoc}
     */
    public String format(DataValue value, int format) {
        if (value == null) {
            throw new NullPointerException("value is null");
        }
        if (!isTypeSupported(value.getType())) {
            throw new IllegalArgumentException(value.getType() + " data type not supported");
        }
        String res;
        switch (format) {
            case FORMAT_SERIALIZED:
                res = formatSerializedString(value);
                break;
            case FORMAT_SHORT_LOG:
                res = formatShortLogString(value);
                break;
            case FORMAT_MAX_LOG:
                res = formatMaxLogString(value);
                break;
            default:
                throw new IllegalArgumentException("unsupported format " + format);
        }
        return res;
    }

    /**
     * Return value formatted in serialized form.
     *
     * @param value value to be serialized to String
     * @return string with serialized value
     */
    public abstract String formatSerializedString(DataValue value);

    /**
     * Returns short info on value for usage in logs. Name of type and compact representation of value expected.
     *
     * @param value value to be formatted for usage in log
     * @return short info on value for usage in logs
     */
    public abstract String formatShortLogString(DataValue value);

    /**
     * Returns expanded info on value for usage in logs. Names of classes of type and value.
     * Values of inner fields expected if needed of compact representation of value.
     *
     * @param value value to be formatted for usage in log
     * @return expanded info on value for usage in logs
     */
    public abstract String formatMaxLogString(DataValue value);

    /**
     * Deserializes values from string (with serizlized value of supported data type)
     *
     * @param serializedValue string with serialized value
     * @return value that was serialized to string
     */
    public abstract DataValue valueOf(String serializedValue);
}
